Skip to content

Rebuild glyph instance editing and canvas runtime#47

Merged
kostyafarber merged 13 commits into
mainfrom
the-big-refactor
May 20, 2026
Merged

Rebuild glyph instance editing and canvas runtime#47
kostyafarber merged 13 commits into
mainfrom
the-big-refactor

Conversation

@kostyafarber
Copy link
Copy Markdown
Collaborator

Summary

  • Unifies displayed glyph access around glyph instances with geometry, render, and optional edit surfaces.
  • Reworks source-backed geometry/render caches so source edits update reactive metrics, sidebearings, outlines, handles, and tool hit paths without broad geometry rebuilds.
  • Migrates select, pen, sidebar, debug, perf, and test helpers off legacy editGlyphSource/previewInstance accessors.
  • Adds public mutation API docs and exposes editor gesture state for drag-aware systems like edge pan.
  • Includes related shell/view resizing cleanup from the branch.

Verification

  • pre-commit hooks passed on commits, including prettier, oxlint, tsgo typecheck, deadcode, and vitest.
  • pnpm --filter @shift/desktop typecheck
  • pnpm --filter @shift/desktop test

End-to-end replacement of the old TextRunController. New layered architecture:

Layer 0 (pure, immutable geometry):
  Cell = GlyphCell | LineBreak (tagged union, replaces flat GlyphRef + magic ".newline")
  Positioner: harfbuzz-shaped no-shape positioner; will swap in shaping later
  TextLayout: splitParagraphs → segmentRuns → position → assembleLayout, hit-test, pointAt
  Caret: immutable cursor projection over a TextLayout
  All built against the real MutatorSans font via shared testUtils.ts

Layer 1 (state):
  TextBuffer: per-field signals (cells/cursor/anchor/originX), #update(patch) batching
  TextInteraction: editing slot + suspended + hover (composite drill-through dropped)
  TextRun: composes both, exposes layout/caret/selectionRects ComputedSignals,
    threads goalX for vertical nav, wraps mutations to keep editing state coherent
  TextRuns: per-glyph store with reactive active and serialize/deserialize

Wire-up:
  Editor: textRuns + textRun getter; persistence effect tracks active run buffer
  HiddenTextInput: rewired to editor.textRun.{insert,delete,move...}
  Text tool, TextRunHover, TextRunEdit: minimal stubs to be refilled
  Validation schema: PersistedTextRun now { buffer: TextBufferSnapshot } (Cell-shaped)

Renames + tightening:
  GlyphRef → Cell, unicode → codepoint, glyphRefFromUnicode → cellFromCodepoint
  GlyphView gains a bounds getter (delegates to font.getBbox)
  Positioner is the single class; interface dropped (structural typing)

Deferred for follow-up:
  Caret.nextLine/previousLine, TextRun vertical/word/line-edge nav
  TextLayout.shapeHitTest
  TextRunRenderer (canvas-side draw, indicator-pattern class)
  TextRunHover + TextRunEdit real impls
  Composite drill-through (own class when rebuilt)

Tests: 542 desktop + 112 validation passing, full typecheck clean.
Bundles four threads that share consumers (Editor.ts, tools/text/Text.ts)
and cannot be split without breaking typecheck on intermediate commits:

- Promote text/* out of tools/ into lib/text; finalize Cell + TextRun
  with stable cell ids carried through the persistence schema.
- Replace startEditSession(glyphName, unicode?) with a GlyphHandle
  object on NativeBridge; ripple through every test caller.
- Render text runs in the editor scene via rendering/Text.ts; rework
  Editor focused-glyph reactivity around GlyphAnchor + Positioner.
- Renames: graphics ReglHandleContext → Gpu, tools text
  TypingBehaviour → TypingBehavior, tool class Text → TextTool.
Rename the core editing crate to shift-edit, move native bridge ownership from shift-node to shift-bridge, and introduce shift-wire as the DTO boundary shared by bridge adapters.

Font loading moves into shift-backends, while shift-edit owns edit session state, glyph structure restoration, interpolation, and active edit mutation semantics. The bridge now uses typed errors and active edit state instead of the old edit-session/snapshot paths.

This is a migration checkpoint: Rust workspace tests exclude the NAPI cdylib, while the native bridge is verified through the NAPI debug build. Renderer call sites are still being moved onto the new API in later commits.

Verified with cargo test --workspace --exclude shift-bridge, cargo clippy --workspace --all-targets --exclude shift-bridge -- -D warnings, and pnpm --filter shift-bridge run build:debug.
Add @shift/bridge as the TypeScript package boundary for constructing the native bridge, and make @shift/types the generated DTO surface for NAPI bridge types.

Replace the old ts-rs generated type tree with bridge declaration generation, wire the package into pnpm/turbo, and expose the bridge through preload under the new Bridge naming. The package includes a CommonJS entry so Electron preload can require it at runtime.

This intentionally does not finish renderer model migration; it only establishes how TypeScript imports the native bridge and its DTOs.

Verified with pnpm --filter @shift/bridge typecheck and require('@shift/bridge') from apps/desktop.
- introduce Glyph/GlyphSource/Source edit boundaries

- move reusable glyph geometry into @shift/glyph-state

- replace node position updates with source positions and edit drafts

- remove snapping and clean up select/pen editing flows

- update bridge APIs, clipboard handling, tests, and docs
Rework the editor architecture around explicit source/preview glyph state, reactive render models, and tool-owned interaction state.

- split editor state into focused domains for glyph/source context, camera/input, selection, hover, text editing, and glyph display
- replace viewport/rendering manager paths with Camera, Renderer, render frames, canvas surfaces, and CanvasItem render boundaries
- move selection, hover, hit, bounding box, marquee, and segment affordance logic out of loose renderer/types modules and into editor or tool-owned surfaces
- introduce source-aware glyph state, sparse position patches, render models, keyed cache support, and outline/path invalidation so edit previews can update without rebuilding full geometry
- update select and pen tools to use richer domain hit/query surfaces and tool-local drawing components
- consolidate handle rendering around overlay/marker backends and keep marker upload ownership inside rendering infrastructure
- add signal debugging, dependency naming, agent skill sync, jsdoc guidance, and focused tests for source geometry freshness, drafts, selection, camera, bounding boxes, and rendering primitives
- update Rust bridge/edit-session paths for sparse position patching and source-aware glyph synchronization
@kostyafarber kostyafarber merged commit ee19e8c into main May 20, 2026
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant